import tkinter as tk

class tkTabel:
    root=None   #上层控件 
    frame_root=None  #顶层框架   
    frame=None  #框架
    frame_tou=None #表头框架
    scroll_y=None  #滚动条
    #表头数据结构 list=[[字段1,长度],[字段2,长度],[字段3,长度],[字段4,长度],......[字段n,长度]]  字段为str 长度=字符个数
    biaotou=[] 
    column_index=0 #显示的起始列    
    tabel_data=[]   #表数据 二维列表 数据类型=tk.Stringvar() 绑定到每个单元格
    addata=[]      #数据源 完整数据
    start_index=0  #滚动显示数据源时候的起始行数
    select_index=-1#记录当前表中被选择的行数 数据源中需要 + start_index  -1代表没有选择
    ad_select_index=-1  #记录数据源中被选择的行数  -1代表没有选择
    is_show=False    #如果运行过show()函数后就不能再次运行show()函数
    is_gundong=False #是否需要滚动
    rows=0         #列表行数
    columns=0      #列表列数
    items=[]       #表中的所有行列 二维表形式
    items_tou=[]   #表头行 列表
    old_item=None  #上一个被选择的行 选择新行时候需要复位外观
    old_item_color=None      #上一个被选择行的背景颜色
    old_xy=[0,0]   #上一个坐标 滚动时候使用
    def __init__(self,root,rows=10): #root=父窗口  rows=表格行数 默认10行
        self.root=root
        self.rows=rows
        self.frame_root=tk.Frame(root)
        self.frame=tk.Frame(self.frame_root)
        self.frame_tou=tk.Frame(self.frame_root)
        self.scroll_y=tk.Scrollbar(root,command=self.gundong_y) 
           
    def gundong_y(self,p1=None,p2=None,p3=None):        
        if self.old_item != None:  #滚动时候清除选择样式
            for bg in self.old_item:
                bg['bg']=self.old_item_color
            self.old_item=None
            self.ad_select_index=-1
            self.select_index=-1           
        if(p1=='moveto'):
            v=float(p2)
            self.scroll_y.set(v,v+0.1)            
        else:
            v=self.scroll_y.get()
            v=v[:2]
            if(p2=='1'):
                add=0.05
            else:
                add=-0.05            
            self.scroll_y.set(v[0]+add,v[1]+add)
        ver=self.scroll_y.get()
        ver=ver[0]        
        self.start_index=int(len(self.addata)*ver)
        if(self.start_index>(len(self.addata)-self.rows)):
            self.start_index=len(self.addata)-self.rows                    
        self.set_data()   
    
    def qinkong(self,p1):
        if(p1==1):            
            self.column_index+=80 
            if(self.column_index>0):
                self.column_index=0 
                return          
        else:            
            self.column_index-=80                
        self.frame_root.place(x=self.column_index,y=0)   
    def MouseWheel(self,event): #鼠标滚轮事件
        if(event.delta>0):
            self.gundong_y('up','-1')
        else:
            self.gundong_y('down','1')
    
    def show(self):
        if(self.is_show):
            return        
        self.scroll_y.pack(side=tk.RIGHT,fill=tk.Y) #创建滚动条 右边        
        self.frame_root.place(x=0,y=0)
        self.frame_tou.pack(pady=0,padx=5,fill=tk.BOTH)
        for i in range(len(self.biaotou)):
            lab_tem=tk.Label(self.frame_tou,text=self.biaotou[i][0],width=self.biaotou[i][1],relief='groove',bd=1,bg='#dadaff')
            lab_tem.grid(row=0,column=i,padx=0,pady=0)
            
            self.items_tou.append(lab_tem)
        self.frame.pack(pady=0,padx=5,fill=tk.BOTH)
        self.columns=len(self.biaotou)
        for i in range(self.rows):
            item_tem=[]
            texts1=[]
            for j in range(len(self.biaotou)):
                text_tem=tk.StringVar()
                lab=tk.Label(self.frame,textvariable=text_tem,width=self.biaotou[j][1],relief='groove',bd=1,anchor='w') 
                if(i%2==1):
                    lab['bg']='#eeeeff'
                else:
                    lab['bg']='white'
                lab.grid(row=i,column=j,padx=0,pady=0)
                lab.bind('<Button-1>',self.is_select)                
                item_tem.append(lab)
                texts1.append(text_tem)
            self.items.append(item_tem)
            self.tabel_data.append(texts1)
        self.is_show=True
        #self.frame.bind('<MouseWheel>',self.MouseWheel)
        self.root.bind('<B1-Motion>',self.mouse_motion)
        self.root.bind('<MouseWheel>',self.MouseWheel)
    def is_select(self,event):        
        if self.old_item != None:
            for bg in self.old_item:
                bg['bg']=self.old_item_color
                # bg['fg']='black'        
        self.old_item_color=event.widget['bg']
        data=[data for data in self.items if event.widget in data][0]
        col=data.index(event.widget)
        row=self.items.index(data)
        self.select_index=row
        self.ad_select_index=row+self.start_index
        if(self.ad_select_index>=len(self.addata)): #如果选择到空行上 清空选择状态 跳出
            self.ad_select_index=-1
            self.select_index=-1
            return

        for bg in data:
            bg['bg']='#0080c0'
            # bg['fg']='white'
        self.old_item=data               
    def set_data(self):        
        my_rows=0
        my_rows_tem=len(self.addata)-self.start_index        
        if(my_rows_tem>=self.rows):
            my_rows=self.rows
        else:
            my_rows=my_rows_tem       
        for i in range(my_rows):
            i_tem=i+self.start_index
            for j in range(self.columns):
                self.tabel_data[i][j].set(self.addata[i_tem][j])
    def cls(self):
        for i in self.tabel_data:
            for j in i:
                j.set('') 
        self.ad_select_index=-1
        self.select_index=-1
        self.start_index=0   
    def biaotou_add(self,name,width):
        self.biaotou.append([name,width])
    def select_get(self):        
        if(self.ad_select_index==-1):
            return        
        return self.addata[self.ad_select_index]
    def mouse_motion(self,event):        
        if(event.y_root>self.old_xy[1]+50):            
            self.gundong_y('down','-1')  
            self.old_xy=[event.x_root,event.y_root]          
        elif(event.y_root<self.old_xy[1]-50):
            self.gundong_y('up','1')
            self.old_xy=[event.x_root,event.y_root]
        elif(event.x_root>self.old_xy[0]+60):
            self.qinkong(-1)
            self.old_xy=[event.x_root,event.y_root]
        elif(event.x_root<self.old_xy[0]-60):
            self.qinkong(1)
            self.old_xy=[event.x_root,event.y_root]        
if __name__ == "__main__":
    root=tk.Tk()
    root.geometry('450x420')
    root.resizable(0,0)
    tabel=tkTabel(root)
    tabel.rows=20
    tabel.biaotou=[['id',5],['日期',10],['名字',5],['地址',30],['备注',20],['dd',20],['dd1',20],['dd2',20],['dd3',20],['dd4',20]]
    tabel.show()
    suju=[]
    for i in range(101):
        suju_tem=[]
        for j in range(tabel.columns):
            suju_tem.append('%d-%d'%(i,j))
        suju.append(suju_tem)
    tabel.addata=suju    
    tabel.set_data() 
    root.mainloop()
    